/**
 *
 * SPDX-License-Identifier: Apache-2.0
 */

const {Utils:utils} = require('fabric-common');
const logger = utils.getLogger('E2E setAnchorPeers');

const tape = require('tape');
const _test = require('tape-promise').default;
const test = _test(tape);

const Client = require('fabric-client');
const fs = require('fs');
const path = require('path');

const _commonProto = require('fabric-protos').common;
const testUtil = require('../util.js');
const e2eUtils = require('./e2eUtils.js');
const channel_name = process.env.channel ? process.env.channel : 'mychannel';// can use "channel=<name>" to control the channel name from command line
const anchorPeerTXFile = path.join(__dirname, '../../fixtures/crypto-material/channel-config/mychannel-org1anchor.tx');
const org = 'org1';


/**
 * This would take effect in next block. Tt is recommended to register a block event after calling this.
 * @param {Channel} channel
 * @param {string} anchorPeerTxFile file path. Anchor peer file can be generated by `configtxgen` with `outputAnchorPeersUpdate` option
 * @param {Orderer} orderer
 * @returns {Promise<BroadcastResponse>}
 */
const updateAnchorPeers = async (channel, anchorPeerTxFile, orderer) => {
	const client = channel._clientContext;
	const channelConfig_envelop = fs.readFileSync(anchorPeerTxFile);
	const channelConfig = client.extractChannelConfig(channelConfig_envelop);
	const signature = client.signChannelConfig(channelConfig);

	const request = {
		config: channelConfig,
		signatures: [signature],
		name: channel.getName(),
		orderer,
		txId: client.newTransactionID()
	};

	const result = await client.updateChannel(request);

	logger.info(`set AnchorPeers on channel ${channel.getName()}`, result);
	return result;
};


test('\n\n***** End-to-end flow: setAnchorPeers *****\n\n', async (t) => {
	testUtil.resetDefaults();
	Client.addConfigFile(path.join(__dirname, './config.json'));
	const ORGS = Client.getConfigSetting('test-network');

	const client = new Client();

	const caRootsPath = ORGS.orderer.tls_cacerts;
	const data = fs.readFileSync(path.join(__dirname, caRootsPath));
	const caroots = Buffer.from(data).toString();

	const tlsInfo = await e2eUtils.tlsEnroll(org);
	client.setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key);

	const store = await Client.newDefaultKeyValueStore({path: testUtil.storePathForOrg(ORGS[org].name)});
	client.setStateStore(store);
	const cryptoSuite = Client.newCryptoSuite();
	cryptoSuite.setCryptoKeyStore(Client.newCryptoKeyStore({path: testUtil.storePathForOrg(ORGS[org].name)}));
	client.setCryptoSuite(cryptoSuite);


	await testUtil.getSubmitter(client, t, true /* get the org admin*/, org);
	t.pass(`Successfully enrolled user 'admin' for ${org}`);

	const orderer = client.newOrderer(
		ORGS.orderer.url,
		{
			name: 'new orderer',
			'pem': caroots,
			'ssl-target-name-override': ORGS.orderer['server-hostname']
		}
	);

	const channel = client.newChannel(channel_name);
	const peer = client.newPeer(
		ORGS[org].peer1.requests,
		{
			pem: fs.readFileSync(path.resolve(__dirname, ORGS[org].peer1.tls_cacerts)).toString(),
			'ssl-target-name-override': ORGS[org].peer1['server-hostname']
		}
	);
	channel.addPeer(peer, ORGS[org].mspid);
	await channel.initialize();


	const ChannelPeers = channel.getPeers();
	const eventHub = channel.newChannelEventHub(ChannelPeers[0]);
	eventHub.connect(true);


	const blockEventPromise = new Promise((resolve, reject) => {
		const block_registration_number = eventHub.registerBlockEvent((block) => {
			if (block.data.data.length !== 1) {
				t.comment('Config block must only contain one transaction');
				return;
			}
			const envelope = block.data.data[0];
			const channel_header = envelope.payload.header.channel_header;
			t.comment(JSON.stringify(channel_header));

			if (channel_header.type !== _commonProto.HeaderType.CONFIG) {
				t.comment(`expect block of type "CONFIG" (${_commonProto.HeaderType.CONFIG}), but got "${channel_header.type}" instead`);
				return;
			}

			t.pass('Successfully update anchor peers.');
			eventHub.unregisterBlockEvent(block_registration_number, true);
			eventHub.disconnect();
			resolve(block);
		}, (err) => {
			t.fail(`err caught in blockEvent: after update anchor peers, ${err}`);
			eventHub.unregisterBlockEvent(block_registration_number, true);
			eventHub.disconnect();
			reject(err);
		});

	});
	const updateAnchorPromise = async () => {
		const result = await updateAnchorPeers(channel, anchorPeerTXFile, orderer);
		if (result.status !== 'SUCCESS') {
			throw Error(JSON.stringify(result));
		}
	};

	await Promise.all([blockEventPromise, updateAnchorPromise()]);


});

